package com.pig4cloud.pigx.user.schedule;

import cn.hutool.core.util.NumberUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.pig4cloud.pigx.common.customize.redis.utils.RedisUtils;
import com.pig4cloud.pigx.user.constant.RedisKeyPrefixConstant;
import com.pig4cloud.pigx.user.constant.WalletConstant;
import com.pig4cloud.pigx.user.entity.Currency;
import com.pig4cloud.pigx.user.entity.RechargeRecord;
import com.pig4cloud.pigx.user.entity.User;
import com.pig4cloud.pigx.user.enums.RechargeRecordEnum;
import com.pig4cloud.pigx.user.enums.UserEnums;
import com.pig4cloud.pigx.user.service.*;
import com.pig4cloud.pigx.user.wallet.SendServiceCharge;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import okhttp3.OkHttpClient;
import okhttp3.Response;
import org.redisson.api.RLock;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import org.web3j.protocol.Web3j;
import org.web3j.protocol.core.Request;
import org.web3j.protocol.core.methods.response.EthBlockNumber;
import org.web3j.protocol.http.HttpService;

import java.io.IOException;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Objects;
import java.util.Set;

/**
 * 充值记录校验
 *
 */
@Component
@EnableScheduling
@Slf4j
@AllArgsConstructor
public class RechargeRecordCheckTask {

	private final RedisTemplate redis;

	private final RechargeRecordService recordService;

	private final CapitalAccountService accountService;

	private final EmailService emailService;

	private final RedisUtils redisUtils;

	private final SendServiceCharge serviceCharge;

	private final CurrencyService currencyService;

	private final OkHttpClient okHttpClient;

	@Scheduled(fixedDelay = 500)
	public void recordCheckTask() {
		// 分布式锁
		RLock lock = redisUtils.getLock(RedisKeyPrefixConstant.RECHARGE_RECORD_CHECK_LOCK, null);
		if (Objects.isNull(lock)) {
			return;
		}
		try {
			List<RechargeRecord> records = new RechargeRecord().selectList(new QueryWrapper<RechargeRecord>() {{
				eq("state", RechargeRecordEnum.Status.RECHARGING.getValue());
				eq("type", RechargeRecordEnum.Type.CHAIN_RECHARGE.getValue());
			}});
			for (RechargeRecord rechargeRecord : records) {
				try {
					String newestBlockNumber;
					// UNT
					if (UserEnums.Chain.BSC.getValue().equals(rechargeRecord.getPublicChain())) {
						Currency currency = currencyService.getCurrency(WalletConstant.USDT,UserEnums.Chain.BSC.getValue());
						Web3j web3j = Web3j.build(new HttpService(currency.getNodeAddress()));
						Request<?, EthBlockNumber> ethBlockNumberRequest = web3j.ethBlockNumber();
						EthBlockNumber send = ethBlockNumberRequest.send();
						newestBlockNumber = String.valueOf(send.getBlockNumber());
					}
					// 波场
					else if (UserEnums.Chain.TRON.getValue().equals(rechargeRecord.getPublicChain())) {
						Currency currency = currencyService.getCurrency(WalletConstant.USDT,UserEnums.Chain.TRON.getValue());
						okhttp3.Request request = new okhttp3.Request.Builder()
								.url(currency.getNodeAddress() + "walletsolidity/getnowblock")
								.get()
								.addHeader("Accept", "application/json")
								.build();

						Response response = null;
						try {
							response = okHttpClient.newCall(request).execute();
						} catch (IOException e) {
							throw new RuntimeException(e);
						}
						String res = null;
						try {
							res = response.body().string();
						} catch (IOException e) {
							throw new RuntimeException(e);
						}
						newestBlockNumber = JSONUtil.parseObj(res).getJSONObject("block_header").getJSONObject("raw_data").getStr("number");
					} else {
						break;
					}
					// 区块确认数未达到不做处理
					String blockNumber = rechargeRecord.getBlockNumber();
					BigDecimal sub = NumberUtil.sub(String.valueOf(newestBlockNumber), blockNumber);
					int compare = NumberUtil.compare(NumberUtil.parseLong(String.valueOf(sub)), NumberUtil.parseLong(rechargeRecord.getBlockConfirmationRechargeNumber()));
					if (compare < 0) {
						break;
					}
					rechargeRecord.setBlockConfirmationNumber(String.valueOf(sub));
					// 最小充值数
					int rechargeCompare = rechargeRecord.getQuantity().compareTo(rechargeRecord.getMinimumRechargeNumber());
					if (rechargeCompare < 0) {
						// 未达到最小充值数量 充值失败
						rechargeRecord.setState(RechargeRecordEnum.Status.FAIL.getValue());
						rechargeRecord.setRemark("未达到最小充值数量 无法入账");
						recordService.updateById(rechargeRecord);
						break;
					}
					// 充值成功
					rechargeRecord.setState(RechargeRecordEnum.Status.SUCCESS.getValue());
					// 更新用户的资金账户余额
					recordService.updateById(rechargeRecord);
					// 修改资金账户余额
					accountService.rechargeThen(rechargeRecord);
					// 给用户预存归集所需的手续费
					if (UserEnums.Chain.BSC.getValue().equals(rechargeRecord.getPublicChain())) {
						serviceCharge.bscFee(rechargeRecord);
					}
					if (UserEnums.Chain.TRON.getValue().equals(rechargeRecord.getPublicChain())) {
						serviceCharge.tronFee(rechargeRecord);
					}
					// 删除redis缓存 新增发送邮件缓存
					redis.opsForHash().put(RedisKeyPrefixConstant.RECHARGE_RECORD_EMAIL, rechargeRecord.getId().toString(), 0);
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
			log.info("充值区块确认定时任务 inTime[" + LocalDateTime.now() + "] success");
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			redisUtils.releaseLock(lock);
		}
	}

	@Scheduled(fixedDelay = 500)
	public void recordEmailTask() {
		RLock lock = redisUtils.getLock(RedisKeyPrefixConstant.RECHARGE_RECORD_EMAIL_LOCK, null);
		if (Objects.isNull(lock)) {
			return;
		}

		try {
			Set<String> keys = redis.opsForHash().keys(RedisKeyPrefixConstant.RECHARGE_RECORD_EMAIL);
			for (String key : keys) {
				try {
					// 充值记录
					RechargeRecord rechargeRecord = recordService.getById(key);
					if (rechargeRecord == null) {
						break;
					}
					emailService.sendRechargeRecordEmail(rechargeRecord);
					redis.opsForHash().delete(RedisKeyPrefixConstant.RECHARGE_RECORD_EMAIL, key);
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			redisUtils.releaseLock(lock);
		}
	}

}
